/*
 * @(#)IOUtils.java 1.0 2009/12/01
 */
package stardust.core.io;


import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import stardust.Stardust;
import stardust.core.log.Log;


/**
 * <code>IOUtils</code> 类提供大部分I/O、文件、数据流辅助操作，支持自动管理临时目录。
 * <p/>
 * @see DeleteOnExitHandler
 * @version 1.0, 2009/12/01
 * @author ChenChang
 */
public class IOUtils
{

  /**
   * 日志记录器。
   */
  private static Log log = new Log(DeleteOnExitHandler.class);

  /**
   * 全局临时目录的位置，此目录将在虚拟机终止时删除。
   */
  private static File DELETE_ON_EXIT_DIR;

  /**
   * 标准IO缓冲区大小（64K）。
   */
  public final static int IO_BUFFER_SIZE = 1024 * 32;

  /**
   * EOF标志位。
   */
  public final static int IOS_END_OF_FILE = -1;

  /**
   * 行分隔符。
   */
  public final static String LINE_SEPARATOR = System.getProperty("line.separator");

  /**
   * 文件路径分隔符。
   */
  public final static String FILE_SEPARATOR = System.getProperty("file.separator");

  /**
   * 当前程序的工作目录。
   */
  public final static String WORK_DIR = System.getProperty("user.dir");

  /**
   * 操作系统临时文件夹。
   */
  public final static String TEMP_DIR = System.getProperty("java.io.tmpdir");

  /**
   * 不做任何处理的输入流。
   */
  public final static InputStream BLANK_INPUTSTREAM = new InputStream()
  {

    public int read() throws IOException
    {
      return -1;
    }

  };

  /**
   * 不做任何处理的输出流。
   */
  public final static OutputStream BLANK_OUTPUTSTREAM = new OutputStream()
  {

    public void write(int b) throws IOException
    {
    }

  };

  /**
   * 获取全局临时目录，如果它不存在将会被建立，并在虚拟机终止时删除。
   */
  public static File getTemporarilyDirectory()
  {
    if (DELETE_ON_EXIT_DIR == null)
    {
      File f;

      while ((f = new File(IOUtils.TEMP_DIR, Stardust.SIGNATURE + System.nanoTime())).exists())
        ;

      if (!f.mkdirs())
      {
        throw new Error("无法建立全局临时目录：" + f.getAbsolutePath() + "。");
      }

      DELETE_ON_EXIT_DIR = f;
      IOUtils.deleteOnExit(DELETE_ON_EXIT_DIR);
      Runtime.getRuntime().addShutdownHook(new DeleteOnExitHandler());

      log.trace("建立临时目录：'" + f.getAbsolutePath() + "'，目录将在虚拟机终止时删除。");
    }

    return DELETE_ON_EXIT_DIR;
  }

  /**
   * 获取工作子目录，如果它不存在将会被建立。
   */
  public static File getDirectory(String... dirName)
  {
    File f = new File(WORK_DIR);

    for (int i = 0; i < dirName.length; i++)
    {
      f = new File(f, dirName[i]);

      if (!f.isDirectory() && !f.mkdirs())
      {
        return null;
      }
    }

    return f;
  }

  /**
   * <code>DeleteOnExitHandler</code> 线程类处理任何在虚拟机终止时需要删除的 文件或目录，此前文件及目录须在
   * <code>IOUtils.deleteOnExit(File)</code> 中进行注册。
   * <p/>
   * @see IOUtils
   * @see IOUtils.deleteOnExit
   * @version 1.0, 2009/12/01
   * @author ChenChang
   */
  private static class DeleteOnExitHandler extends Thread
  {

    static final Set<File> files = new HashSet<File>();

    public void run()
    {
      Iterator<File> fileIterator = files.iterator();
      File file;
      boolean type;

      while (fileIterator.hasNext())
      {
        file = fileIterator.next();
        type = file.isDirectory();

        if (file.exists())
        {
          if (!IOUtils.delete(file))
          {
            log.trace("无法删除" + (type ? "目录" : "文件") + ":" + file);
          }
          else
          {
            log.trace("删除" + (type ? "目录" : "文件") + ":" + file);
          }
        }
      }
    }

  }

  /**
   * 递归删除位于文件系统上的指定文件或目录，只有在全部删除成功时返回 <tt>true</tt> 值。
   * 如果在删除任何一个文件（包括子文件或子目录）时失败将返回 <tt>false</tt> 值。
   * <p/>
   * @param file
   * 希望删除的文件或目录，它是一个
   * <code>File</code> 对象。
   * <p/>
   * @return 任何一个文件或目录删除失败返回 <tt>false</tt> 值。
   */
  public static boolean delete(File file)
  {
    File[] files;
    boolean flag = true;

    if (file.exists())
    {
      if (file.isDirectory())
      {
        files = file.listFiles();

        for (int i = 0; i < files.length; i++)
        {
          if (files[i].isDirectory())
          {
            flag = (IOUtils.delete(files[i]) & flag);
          }
          else
          {
            flag = (files[i].delete() & flag);
          }
        }
      }

      flag = (file.delete() & flag);
    }

    return flag;
  }

  /**
   * 在虚拟机退出时删除位于文件系统上的指定目标文件或目录。 只有在全部删除成功时返回 true。 如果在删除任何一个文件包括子文件或目录时失败将返回
   * false。
   * <p/>
   * @param file
   * 希望删除的文件或目录
   */
  public static void deleteOnExit(File file)
  {
    DeleteOnExitHandler.files.add(file);
  }
  
  public static void readToWrite(String file, OutputStream out) throws IOException
  {
    IOUtils.readToWrite(new File(file), out);
  }

  public static void readToWrite(File file, OutputStream out) throws IOException
  {
    FileInputStream in = new FileInputStream(file);
    
    try
    {
      IOUtils.readToWrite(in, out);
    }
    finally
    {
      in.close();
    }
  }
  
  public static void readToWrite(InputStream in, OutputStream out) throws IOException
  {
    int offset;
    byte[] buff = new byte[IO_BUFFER_SIZE];

    while ((offset = in.read(buff)) != IOS_END_OF_FILE)
    {
      out.write(buff, 0, offset);
    }
  }

  public static byte[] read(String file) throws IOException
  {
    return read(new File(file));
  }

  public static byte[] read(File file) throws IOException
  {
    FileInputStream fis = new FileInputStream(file);

    try
    {
      return read(fis);
    }
    finally
    {
      fis.close();
    }
  }

  public static byte[] read(InputStream ins) throws IOException
  {
    int len;
    byte[] buff = new byte[IO_BUFFER_SIZE];
    ByteArrayOutputStream byteBuff = new ByteArrayOutputStream();

    while ((len = ins.read(buff)) != IOS_END_OF_FILE)
    {
      byteBuff.write(buff, 0, len);
    }

    return byteBuff.toByteArray();
  }

  public static String read(Reader rd) throws IOException
  {
    int len;
    char[] buff = new char[IO_BUFFER_SIZE];
    StringBuffer sb = new StringBuffer();

    while ((len = rd.read(buff)) != IOS_END_OF_FILE)
    {
      sb.append(buff, 0, len);
    }

    return sb.toString();
  }

  public static byte[] read(InputStream ins, int len) throws IOException
  {
    int readLen, offset;
    byte[] buff = new byte[len];

    offset = 0;
    while (offset < len && (readLen = ins.read(buff, offset, len - offset)) != IOS_END_OF_FILE)
    {
      offset += readLen;
    }

    if (offset != len)
    {
      throw new IOException("EOF");
    }

    return buff;
  }

  public static void write(String file, byte[] data, boolean append) throws IOException
  {
    write(new File(file), data, append);
  }

  public static void write(File file, byte[] data, boolean append) throws IOException
  {
    FileOutputStream fos = new FileOutputStream(file, append);

    try
    {
      write(fos, data);
    }
    finally
    {
      fos.close();
    }
  }

  public static void write(OutputStream ous, byte[] data) throws IOException
  {
    ous.write(data);
  }

}
